﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Data;
using System.Data.Linq;
using System.IO;
using System.Linq;
using System.Xml.Linq;
using System.Linq.Dynamic;
using System.Runtime.Serialization.Json;
using System.Reflection;
using System.Text;
using System.Xml;
using IntraVision.Xml;

namespace IntraVision.Web
{
    public class TableController<TEntity, TDataContext> : ConfigurableController
        where TEntity : class, new()
        where TDataContext : DataContext, new()
    {
        protected string _PageTemplate = "table";
        protected string _TableTemplate = "tabledata";

        private int _CurrentPage = 1;
        private int _PageSize = 25;
        private int _RowCount = 0;

        private string _Title;
        private List<ColumnInfo> _Columns;

        public List<ColumnInfo> Columns
        {
            get { return _Columns; }
            set { _Columns = value; }
        }

        public string Title
        {
            get { return _Title; }
            set { _Title = value; }
        }

        private string _Where;
        private string _FixedWhere;

        public string FixedWhere
        {
            get { return _FixedWhere; }
            set { _FixedWhere = value; }
        }

        public string Where
        {
            get { return _Where; }
            set { _Where = value; }
        }
        private string _OrderBy;

        public string OrderBy
        {
            get { return _OrderBy; }
            set { _OrderBy = value; }
        }
        private string _Select;

        public string Select
        {
            get { return _Select; }
            set { _Select = value; }
        }

        public int CurrentPage
        {
            get { return _CurrentPage; }
            set { _CurrentPage = value; }
        }

        public int PageSize
        {
            get { return _PageSize; }
            set { _PageSize = value; }
        }

        public int RowCount
        {
            get { return _RowCount; }
            set { _RowCount = value; }
        }

        private int _PageCount;

        public int PageCount
        {
            get { return _PageCount; }
            set { _PageCount = value; }
        }
        
        public TableController() : base()
        {
            _Columns = new List<ColumnInfo>();
            _ConfiguratorPath = Settings["EntityType"];
        }

        public virtual void Default()
        {
            TDataContext dc = new TDataContext();
            IQueryable entities = dc.GetTable<TEntity>();

            Initialize();

            if (!string.IsNullOrEmpty(_Where))
                entities = entities.Where(_Where);
            if (!string.IsNullOrEmpty(_FixedWhere))
                entities = entities.Where(_FixedWhere);

            string select = GenerateSelectClause();
            if (!string.IsNullOrEmpty(select))
                entities = entities.Select(select);
            
            if (!string.IsNullOrEmpty(_OrderBy))
                entities = entities.OrderBy(_OrderBy);

            Paginate(entities);

            int skip = (_CurrentPage - 1) * _PageSize;
            entities = entities.Skip(skip).Take(_PageSize);

            XmlElement root = ResultXml.AppendElement("table");
            GenerateSettingsXml(root);
            GenerateHeaderXml(root);

            XmlElement xd = root.AppendElement("data");
            
            ColumnInfo[] tmpColumns = _Columns.OrderBy(c => c.Position).Where(c => c.Hidden == false || c.LoadAlways == true).ToArray();
            foreach (var entity in entities)
                AppendEntity(entity, xd,tmpColumns);

            if (Request["xml"] != null) RenderHtml("default",null);
            else if (Request["ajax"] != null) RenderHtml(_TableTemplate,null);
            else RenderHtml(_PageTemplate,null);
        }

        protected virtual void Paginate(IQueryable entities)
        {
            if (!Int32.TryParse(Request["page"], out _CurrentPage))
                _CurrentPage = 1;

            _RowCount = entities.Count();
            _PageCount = (int)Math.Ceiling((decimal)_RowCount / _PageSize);
            if (_CurrentPage < 0 || _CurrentPage > _PageCount ) _CurrentPage = 1;
        }

        protected virtual void AppendEntity(object entity, XmlElement xd, ColumnInfo[] columns)
        {
            XmlElement xr = xd.AppendElement("row");
            foreach (ColumnInfo column in columns)
            {
                XmlElement xc = xr.AppendElement(column.Id, column.GetValue(entity));
                if (column.Hidden) xc.AppendAttribute("hidden", true);
            }
        }

        protected virtual void Initialize()
        {
            LoadConfigurator();
            InitializeSettings();

            _Select = Request["select"];
            _Where = Request["where"];
            _OrderBy = Request["orderby"];

            InitializeColumns();
        }

        protected virtual void InitializeColumns()
        {
            if (_Configurator != null)
            {
                foreach (XmlElement node in _Configurator.SelectNodes("/configurator/columns/column"))
                    _Columns.Add(new ColumnInfo(node));
            }
            if (!string.IsNullOrEmpty(_Select))
            {
                try
                {
                    string[] ids = Request["select"].ToLower().Trim().Split(',');

                    int pos = 0;
                    for (int i = 0; i < _Columns.Count;i++ )
                    {
                        _Columns[i].Hidden = true;
                        _Columns[i].Position = Int32.MaxValue;
                    }

                    for (int i = 0; i < ids.Length; i++)
                    {
                        ColumnInfo column = _Columns.Single(c => c.Id.ToLower() == ids[i].Trim());
                        if ( column != null)
                        {
                            column.Hidden = false;
                            column.Position = pos;
                            pos++;
                        }
                    }

                    _Columns.OrderBy(c => c.Position);
                }
                catch { }
            }
        }

        protected string GenerateSelectClause()
        {
            StringBuilder select = new StringBuilder("new (");
            foreach (ColumnInfo column in _Columns.OrderBy(c=>c.Position).Where(c=>c.Hidden==false || c.LoadAlways==true))
            {
                select.Append(column.SelectExpression).Append(" as " ).Append(column.Id).Append(", ");
            }
            select = select.Remove(select.Length - 2, 2).Append(")");
            return select.ToString();
        }

        protected virtual void GenerateHeaderXml(XmlElement root)
        {
            XmlElement xh = root.AppendElement("header");
            foreach (ColumnInfo column in _Columns)
                column.AppendHeaderXml(xh);
        }

        protected virtual void GenerateSettingsXml(XmlElement root)
        {
            XmlElement xs = root.AppendElement("settings");
            xs.AppendCDataElement("select", _Select);
            xs.AppendCDataElement("where", _Where);
            xs.AppendCDataElement("orderby", _OrderBy);

            xs.AppendElement("page", _CurrentPage);
            xs.AppendElement("pagecount", _PageCount);
            xs.AppendElement("pagesize", _PageSize);
            xs.AppendElement("rowcount", _RowCount);
        }
    }
}
